using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using OracleInternal.Common;
using OracleInternal.Secure.Network;

namespace OracleInternal.Network
{
	internal class TcpsTransportAdapter : TcpTransportAdapter
	{
		private static int SSL_VERSION_SSL_2;

		private static int SSL_VERSION_SSL_3;

		private static int SSL_VERSION_TLS10;

		private static int SSL_VERSION_TLS11;

		private static int SSL_VERSION_TLS12;

		private static int SSL_VERSION_DEF;

		private static int EphemeralKeySet;

		protected bool DNMatching;

		protected SslStream m_sslstream;

		protected bool m_DNMatched = true;

		private static MethodInfo MacOSConvert_m;

		public override bool NeedReneg => true;

		static TcpsTransportAdapter()
		{
			SSL_VERSION_SSL_2 = 12;
			SSL_VERSION_SSL_3 = 48;
			SSL_VERSION_TLS10 = 192;
			SSL_VERSION_TLS11 = 768;
			SSL_VERSION_TLS12 = 3072;
			SSL_VERSION_DEF = 240;
			EphemeralKeySet = 32;
			MacOSConvert_m = null;
			try
			{
				string strA = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
				if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || string.Compare(strA, 0, ".NETCoreApp,Version=v3", 0, 22, StringComparison.InvariantCulture) != 0)
				{
					return;
				}
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				string str = "src.Core3.bin.release.netstandard2._1.OracleInternal.Core3.dll";
				if (ProviderConfig.m_bTraceLevelNetwork)
				{
					Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate, "Convert for MacOS: Retrieving the assembly AND the convert method from it...");
				}
				string name = new AssemblyName(executingAssembly.FullName).Name + "." + str;
				using Stream stream = executingAssembly.GetManifestResourceStream(name);
				byte[] array = new byte[stream.Length];
				stream.Read(array, 0, array.Length);
				MacOSConvert_m = Assembly.Load(array).GetType("OracleInternal.Core3.TcpsTransportAdapterCore3", throwOnError: true, ignoreCase: true)!.GetMethod("ConvertForMacOS", new Type[2]
				{
					typeof(byte[]),
					typeof(string)
				});
				if (MacOSConvert_m == null)
				{
					throw new NetworkException(12557);
				}
			}
			catch (Exception ex)
			{
				MacOSConvert_m = null;
				if (ProviderConfig.m_bTraceLevelNetwork)
				{
					Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate, "Convert for MacOS : Retrieval of assembly or the convert method failed. Exception:");
					Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate, ex.ToString());
				}
			}
		}

		internal TcpsTransportAdapter(NameValueCollection socketOptions)
			: base(socketOptions)
		{
		}

		internal TcpsTransportAdapter(ConnectionOption conOption, TcpClient tcpClient)
			: base(conOption, tcpClient)
		{
			m_sslstream = new SslStream(m_client.GetStream());
		}

		public override void Renegotiate(ConnectionOption conOption)
		{
			Negotiate(conOption);
		}

		public override void Connect(ConnectionOption conOption)
		{
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Entry, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Connect);
			}
			base.Connect(conOption);
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Exit, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Connect);
			}
		}

		public override Stream ConnectIterate()
		{
			m_sslstream = null;
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Entry, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ConnectIterate);
			}
			if (base.ConnectIterate() != null)
			{
				Negotiate(m_conOption);
			}
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Exit, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ConnectIterate);
			}
			return m_sslstream;
		}

		public override ITransportAdapter Answer(ConnectionOption conOption)
		{
			if (m_listener == null)
			{
				return null;
			}
			return new TcpsTransportAdapter(conOption, m_listener.AcceptTcpClient());
		}

		public override void Disconnect()
		{
			lock (m_discLock)
			{
				if (m_sslstream != null)
				{
					m_sslstream.Close();
					m_sslstream = null;
				}
			}
			base.Disconnect();
		}

		public override Stream GetStream()
		{
			return m_sslstream;
		}

		public override Socket GetSocket()
		{
			return null;
		}

		public override Socket GetTestSocket()
		{
			return base.GetTestSocket();
		}

		public override void Send(OraBuf OB)
		{
			OraArraySegment[] the_ByteSegments = OB.the_ByteSegments;
			try
			{
				if (OB.the_ByteSegments_Count == 2)
				{
					m_sslstream.Write(OB.m_buf, 0, OB.m_curlen);
					return;
				}
				for (int i = 0; i < OB.the_ByteSegments_Count; i++)
				{
					OraArraySegment oraArraySegment = the_ByteSegments[i];
					m_sslstream.Write(oraArraySegment.Array, oraArraySegment.Offset, oraArraySegment.Count);
				}
			}
			catch (Exception inner)
			{
				throw new NetworkException(12571, inner);
			}
		}

		private void Negotiate(ConnectionOption conOption)
		{
			SqlNetOraConfig sNOConfig = conOption.SNOConfig;
			X509CertificateCollection x509CertificateCollection = null;
			X509Certificate2 x509Certificate = null;
			SslProtocols enabledSslProtocols = (SslProtocols)(0xC0 | (SSL_VERSION_TLS11 | SSL_VERSION_TLS12));
			Hashtable HT = null;
			string Dir = null;
			string WP = null;
			string text = null;
			string text2 = "";
			Exception ex = null;
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Entry, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate);
			}
			text = sNOConfig.SSLVersion;
			if (!string.IsNullOrEmpty(text) && !text.Equals("0"))
			{
				switch (text)
				{
				case "1.0":
					enabledSslProtocols = SslProtocols.Tls;
					break;
				case "1.1":
					enabledSslProtocols = (SslProtocols)SSL_VERSION_TLS11;
					break;
				case "1.2":
					enabledSslProtocols = (SslProtocols)SSL_VERSION_TLS12;
					break;
				case "1.0 or 1.1":
				case "1.1 or 1.0":
					enabledSslProtocols = (SslProtocols)(SSL_VERSION_TLS11 | SSL_VERSION_TLS10);
					break;
				case "1.2 or 1.1":
				case "1.1 or 1.2":
					enabledSslProtocols = (SslProtocols)(SSL_VERSION_TLS12 | SSL_VERSION_TLS11);
					break;
				case "1.0 or 1.1 or 1.2":
				case "1.2 or 1.1 or 1.0":
				case "1.2 or 1.0 or 1.1":
				case "1.1 or 1.2 or 1.0":
				case "1.1 or 1.0 or 1.2":
				case "1.0 or 1.2 or 1.1":
					enabledSslProtocols = (SslProtocols)(SSL_VERSION_TLS10 | SSL_VERSION_TLS11 | SSL_VERSION_TLS12);
					break;
				}
			}
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate, "SSLVersion = " + text + ". SSLProtocol = " + enabledSslProtocols.ToString() + ".");
			}
			try
			{
				SqlNetOraConfig.GetWalletLoc(conOption, sNOConfig, out Dir, out HT);
				if (!string.IsNullOrEmpty(Dir))
				{
					text2 = "FILE";
				}
				else if (HT != null)
				{
					text2 = ((string)HT["METHOD"]).ToUpperInvariant();
					if (text2 != null && text2 == "FILE")
					{
						Dir = (string)HT["DIRECTORY"];
					}
				}
				if (text2 == "FILE")
				{
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate, "MY_WALLET_DIRECTORY = " + Dir);
					}
					if (Dir == null)
					{
						throw new NetworkException(-6400);
					}
					Dir = ConfigBaseClass.GetResolvedFileLocation(Dir);
					Dir = SqlNetOraConfig.HandlePathSeparators(Dir);
					if (sNOConfig != null)
					{
						WP = sNOConfig["WALLET_PASSWORD"];
					}
					byte[] array = WalletReader.ReadWallet(Dir, ref WP);
					string strA = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
					if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && string.Compare(strA, 0, ".NETCoreApp,Version=v3", 0, 22, StringComparison.InvariantCulture) == 0 && MacOSConvert_m != null)
					{
						string str = "succeeded.";
						try
						{
							if (ProviderConfig.m_bTraceLevelNetwork)
							{
								Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate, "Converting for MacOS ...");
							}
							array = (byte[])MacOSConvert_m.Invoke(null, new object[2]
							{
								array,
								WP
							});
							if (array == null)
							{
								throw new NetworkException(-6400);
							}
						}
						catch (NetworkException)
						{
							str = "failed.";
							Disconnect();
							throw;
						}
						catch (Exception ex3)
						{
							str = "failed.";
							if (!(ex3 is TargetInvocationException) || ex3.InnerException == null || !(ex3.InnerException is FileNotFoundException) || !ex3.InnerException!.Message.Contains("Pkcs"))
							{
								Disconnect();
								throw new NetworkException(-6400, ex3);
							}
							ex = ex3;
						}
						finally
						{
							if (ProviderConfig.m_bTraceLevelNetwork)
							{
								Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate, "ConvertForMacOS() " + str);
							}
						}
					}
					x509Certificate = new X509Certificate2(array, WP, X509KeyStorageFlags.DefaultKeySet);
					x509CertificateCollection = new X509CertificateCollection(new X509Certificate[1]
					{
						x509Certificate
					});
				}
				else if (text2 == "MCS")
				{
					X509Store x509Store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
					x509Store.Open(OpenFlags.ReadOnly);
					x509CertificateCollection = x509Store.Certificates;
				}
				if (x509CertificateCollection == null || x509CertificateCollection.Count == 0)
				{
					Disconnect();
					throw new NetworkException(-6400);
				}
			}
			catch (NetworkException)
			{
				throw;
			}
			catch (Exception inner)
			{
				Disconnect();
				throw new NetworkException(-6400, inner);
			}
			try
			{
				m_sslstream = new SslStream(m_client.GetStream(), leaveInnerStreamOpen: false, ValidateRemoteCertificate, null);
				m_sslstream.AuthenticateAsClient((conOption.Host == null) ? "" : conOption.Host, x509CertificateCollection, enabledSslProtocols, checkCertificateRevocation: false);
			}
			catch (NetworkException)
			{
				throw;
			}
			catch (AuthenticationException ex6)
			{
				Disconnect();
				if (!m_DNMatched)
				{
					throw new NetworkException(29003, ex6);
				}
				if (ex != null && ex6.InnerException != null && ex6.InnerException!.Message.Contains("handshake failure"))
				{
					throw new NetworkException(542, ex);
				}
				throw new NetworkException(542, ex6);
			}
			catch (Exception inner2)
			{
				Disconnect();
				throw new NetworkException(542, inner2);
			}
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Exit, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.Negotiate);
			}
		}

		private bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors pErrs)
		{
			X509ChainStatusFlags x509ChainStatusFlags = X509ChainStatusFlags.NoError;
			m_DNMatched = true;
			if (ProviderConfig.m_bTraceLevelNetwork)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Entry, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ValidateRemoteCertificate);
			}
			bool flag = false;
			if ((pErrs & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
			{
				X509ChainStatus[] chainStatus = chain.ChainStatus;
				for (int i = 0; i < chainStatus.Length; i++)
				{
					X509ChainStatus x509ChainStatus = chainStatus[i];
					switch (x509ChainStatus.Status)
					{
					case X509ChainStatusFlags.NoError:
						flag = flag;
						break;
					case X509ChainStatusFlags.UntrustedRoot:
						flag = flag;
						break;
					default:
						x509ChainStatusFlags = x509ChainStatus.Status;
						flag = flag || true;
						break;
					}
				}
			}
			if (flag)
			{
				Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ValidateRemoteCertificate, "Status: " + x509ChainStatusFlags);
				return false;
			}
			try
			{
				DNMatching = (m_conOption.SSLServerDNMatching.HasValue ? m_conOption.SSLServerDNMatching.Value : m_conOption.SNOConfig.SSLServerDNMatch);
				if (DNMatching)
				{
					if (m_conOption == null || cert == null || string.IsNullOrEmpty(m_conOption.SSLServerDN))
					{
						if (ProviderConfig.m_bTraceLevelNetwork)
						{
							Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ValidateRemoteCertificate, "Relying on .NET hostname matching.");
						}
						return (pErrs & SslPolicyErrors.RemoteCertificateNameMismatch) == 0;
					}
					m_DNMatched = false;
					string sSLServerDN = m_conOption.SSLServerDN;
					string subject = cert.Subject;
					if (string.IsNullOrEmpty(subject))
					{
						return false;
					}
					sSLServerDN = string.Join("", sSLServerDN.Split((string[]?)null, StringSplitOptions.RemoveEmptyEntries));
					subject = string.Join("", subject.Split((string[]?)null, StringSplitOptions.RemoveEmptyEntries));
					sSLServerDN = Regex.Replace(sSLServerDN, "ST=", "S=", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
					m_DNMatched = sSLServerDN.Equals(subject, StringComparison.InvariantCultureIgnoreCase);
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ValidateRemoteCertificate, "DN1 = \"" + sSLServerDN.ToUpperInvariant() + "\" DN2 = \"" + subject.ToUpperInvariant() + "\"");
					}
					if (ProviderConfig.m_bTraceLevelNetwork)
					{
						Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Sqlnet, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ValidateRemoteCertificate, "DN " + (m_DNMatched ? "matched." : "mismatched."));
					}
					return m_DNMatched;
				}
				return true;
			}
			catch (Exception)
			{
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelNetwork)
				{
					Trace.Write(OracleTraceLevel.Network, OracleTraceTag.Exit, OracleTraceClassName.TcpsTransportAdapter, OracleTraceFuncName.ValidateRemoteCertificate);
				}
			}
		}

		public override void BeginAsyncReceives(OraBuf.AsyncReceiveCallback Callback, int AsyncBufferSize)
		{
			m_OraBufSize = AsyncBufferSize;
			m_AsyncRecvCB = Callback;
		}
	}
}
